/*
 * bjm.c
 *
 *  Created on: Jan 12, 2014
 *      Author: Benjamin Groeber, Julian Sanin, Matthaeus Ranigler
 */

#include <stdio.h>
#include <stdlib.h>
#include "bjm.h"

void quit(int status) {
	removeAllSymbols(); // Frees allocated memory.
	exit(status);
}

void setValue(char *symbol, int scopeLevel, data_t data) {
	symbol_t *s = findSymbol(symbol, scopeLevel);
	if(s == NULL) {
		switch(data.type) {
		case INT32:
			addSymbolInt32(symbol, scopeLevel, data.value.int32);
			break;
		case FP32:
			addSymbolFP32(symbol, scopeLevel, data.value.fp32);
			break;
		default:
			/* Do nothing. */
			break;
		}
	} else {
		/* Symbol type is already known. */
		switch(s->type) {
		case INT32:
			switch(data.type) {
			case INT32:
				//printf("sc:0x%x,%d,%d,\n", s, decodeScopeLevel(s->name), scopeLevel);
				//addSymbolInt32(symbol, decodeScopeLevel(s->name), data.value.int32);
				addSymbolInt32(symbol, scopeLevel, data.value.int32);
				//printAllSymbols();
				//exit(0);
				break;
			case FP32:
				//addSymbolInt32(symbol, decodeScopeLevel(s->name), (int)(data.value.fp32));
				addSymbolInt32(symbol, scopeLevel, (int)(data.value.fp32));
				break;
			}
			break;
		case FP32:
			switch(data.type) {
			case INT32:
				//addSymbolFP32(symbol, decodeScopeLevel(s->name), (float)(data.value.int32));
				addSymbolFP32(symbol, scopeLevel, (float)(data.value.int32));
				break;
			case FP32:
				//addSymbolFP32(symbol, decodeScopeLevel(s->name), data.value.fp32);
				addSymbolFP32(symbol, scopeLevel, data.value.fp32);
				break;
			}
			break;
		default:
			/* Do nothing. */
			break;
		}
	}
}

data_t getValue(char *symbol, int scopeLevel) {
	data_t data;
	symbol_t *s = findSymbol(symbol, scopeLevel);
	if(s != NULL) {
		switch(s->type) {
		case INT32:
			data.type = INT32;
			data.value.int32 = s->value.int32;
			break;
		case FP32:
			data.type = FP32;
			data.value.fp32 = s->value.fp32;
			break;
		default:
			data.type = NAN;
		}
	} else {
		data.type = NAN;
	}
	return data;
}

void printValue(data_t data) {
	switch(data.type) {
	case INT32:
		printf("%d\n", data.value.int32);
		break;
	case FP32:
		printf("%.4f\n", data.value.fp32);
		break;
	default:
		/* Do nothing. */
		break;
	}
}

data_t increment(data_t data) {
	data_t newData;
	newData.type = data.type;
	switch(data.type) {
	case INT32:
		newData.value.int32 = data.value.int32 + 1;
		break;
	case FP32:
		newData.value.fp32 = data.value.fp32 + 1.0f;
		break;
	default:
		newData.type = NAN;
	}
	return newData;
}

data_t decrement(data_t data) {
	data_t newData;
	newData.type = data.type;
	switch(data.type) {
	case INT32:
		newData.value.int32 = data.value.int32 - 1;
		break;
	case FP32:
		newData.value.fp32 = data.value.fp32 - 1.0f;
		break;
	default:
		newData.type = NAN;
	}
	return newData;
}

data_t add(data_t left, data_t right) {
	data_t result;
	switch(left.type) {
	case INT32:
		switch(right.type) {
		case INT32:
			result.type = INT32;
			result.value.int32 = left.value.int32 + right.value.int32;
			break;
		case FP32:
			result.type = FP32;
			result.value.fp32 = (float)left.value.int32 + right.value.fp32;
			break;
		}
		break;
	case FP32:
		result.type = FP32;
		switch(right.type) {
		case INT32:
			result.value.fp32 = left.value.fp32 + (float)right.value.int32;
			break;
		case FP32:
			result.value.fp32 = left.value.fp32 + right.value.fp32;
			break;
		}
		break;
	}
	return result;
}

data_t sub(data_t left, data_t right) {
	data_t result;
	switch(left.type) {
	case INT32:
		switch(right.type) {
		case INT32:
			result.type = INT32;
			result.value.int32 = left.value.int32 - right.value.int32;
			break;
		case FP32:
			result.type = FP32;
			result.value.fp32 = (float)left.value.int32 - right.value.fp32;
			break;
		}
		break;
	case FP32:
		result.type = FP32;
		switch(right.type) {
		case INT32:
			result.value.fp32 = left.value.fp32 - (float)right.value.int32;
			break;
		case FP32:
			result.value.fp32 = left.value.fp32 - right.value.fp32;
			break;
		}
		break;
	}
	return result;
}

data_t mul(data_t left, data_t right) {
	data_t result;
	switch(left.type) {
	case INT32:
		switch(right.type) {
		case INT32:
			result.type = INT32;
			result.value.int32 = left.value.int32 * right.value.int32;
			break;
		case FP32:
			result.type = FP32;
			result.value.fp32 = (float)left.value.int32 * right.value.fp32;
			break;
		}
		break;
	case FP32:
		result.type = FP32;
		switch(right.type) {
		case INT32:
			result.value.fp32 = left.value.fp32 * (float)right.value.int32;
			break;
		case FP32:
			result.value.fp32 = left.value.fp32 * right.value.fp32;
			break;
		}
		break;
	}
	return result;
}

data_t divide(data_t left, data_t right) {
	data_t result;
	switch(left.type) {
	case INT32:
		switch(right.type) {
		case INT32:
			result.type = INT32;
			result.value.int32 = left.value.int32 / right.value.int32;
			break;
		case FP32:
			result.type = FP32;
			result.value.fp32 = (float)left.value.int32 / right.value.fp32;
			break;
		}
		break;
	case FP32:
		result.type = FP32;
		switch(right.type) {
		case INT32:
			result.value.fp32 = left.value.fp32 / (float)right.value.int32;
			break;
		case FP32:
			result.value.fp32 = left.value.fp32 / right.value.fp32;
			break;
		}
		break;
	}
	return result;
}

data_t factorial(data_t value) {
	data_t data;
	data.type = INT32;
	switch(value.type) {
	case INT32:
		data.value.int32 = value.value.int32;
		break;
	case FP32:
		data.value.int32 = (int)value.value.fp32;
		break;
	}
	if(data.value.int32 < 0) {
		data.value.int32 = -1;
		return data;
	} else if(data.value.int32 == 0) {
		data.value.int32 = 1;
		return data;
	} else {
		data_t param;
		param.type = INT32;
		param.value.int32 = data.value.int32 - 1;
		data.value.int32 = data.value.int32 * factorial(param).value.int32;
		return data;
	}
}

data_t lessThan(data_t left, data_t right) {
	int l, r;
	if(left.type == FP32) {
		l = (int) left.value.fp32;
	} else {
		l = left.value.int32;
	}
	if(left.type == FP32) {
		r = (int) right.value.fp32;
	} else {
		r = right.value.int32;
	}
	data_t result;
	result.type = INT32;
	result.value.int32 = (l < r);
	return result;
}

data_t greaterThan(data_t left, data_t right) {
	int l, r;
	if(left.type == FP32) {
		l = (int) left.value.fp32;
	} else {
		l = left.value.int32;
	}
	if(left.type == FP32) {
		r = (int) right.value.fp32;
	} else {
		r = right.value.int32;
	}
	data_t result;
	result.type = INT32;
	result.value.int32 = (l > r);
	return result;
}

data_t greaterThanEqual(data_t left, data_t right) {
	int l, r;
	if(left.type == FP32) {
		l = (int) left.value.fp32;
	} else {
		l = left.value.int32;
	}
	if(left.type == FP32) {
		r = (int) right.value.fp32;
	} else {
		r = right.value.int32;
	}
	data_t result;
	result.type = INT32;
	result.value.int32 = (l >= r);
	return result;
}

data_t lessThanEqual(data_t left, data_t right) {
	int l, r;
	if(left.type == FP32) {
		l = (int) left.value.fp32;
	} else {
		l = left.value.int32;
	}
	if(left.type == FP32) {
		r = (int) right.value.fp32;
	} else {
		r = right.value.int32;
	}
	data_t result;
	result.type = INT32;
	result.value.int32 = (l <= r);
	return result;
}

data_t notEqual(data_t left, data_t right) {
	int l, r;
	if(left.type == FP32) {
		l = (int) left.value.fp32;
	} else {
		l = left.value.int32;
	}
	if(left.type == FP32) {
		r = (int) right.value.fp32;
	} else {
		r = right.value.int32;
	}
	data_t result;
	result.type = INT32;
	result.value.int32 = (l != r);
	return result;
}

data_t equal(data_t left, data_t right) {
	int l, r;
	if(left.type == FP32) {
		l = (int) left.value.fp32;
	} else {
		l = left.value.int32;
	}
	if(left.type == FP32) {
		r = (int) right.value.fp32;
	} else {
		r = right.value.int32;
	}
	data_t result;
	result.type = INT32;
	result.value.int32 = (l == r);
	return result;
}

data_t lAnd(data_t left, data_t right) {
	int l, r;
	if(left.type == FP32) {
		l = (int) left.value.fp32;
	} else {
		l = left.value.int32;
	}
	if(left.type == FP32) {
		r = (int) right.value.fp32;
	} else {
		r = right.value.int32;
	}
	data_t result;
	result.type = INT32;
	result.value.int32 = (l && r);
	return result;
}

data_t lOr(data_t left, data_t right) {
	int l, r;
	if(left.type == FP32) {
		l = (int) left.value.fp32;
	} else {
		l = left.value.int32;
	}
	if(left.type == FP32) {
		r = (int) right.value.fp32;
	} else {
		r = right.value.int32;
	}
	data_t result;
	result.type = INT32;
	result.value.int32 = (l || r);
	return result;
}
